package br.com.main.client;

/*
 * Copyright 2010 ArcBees Inc.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

import java.util.ArrayList;

import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.dom.client.KeyDownEvent;
import com.google.gwt.event.dom.client.KeyDownHandler;
import com.google.gwt.event.dom.client.KeyPressEvent;
import com.google.gwt.event.dom.client.KeyPressHandler;
import com.google.gwt.uibinder.client.UiConstructor;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.ui.TextBox;

/**
 * @author Christian Goudreau
 */
public class MaskedTextBox extends TextBox {
    public static final String CEP = "#####-###";
    public static final String DATA = "##/##/####";
    public static final String TELEFONE = "(##) ####-####";
    public static final String TELEFONE_9_DIGITOS = "(##) #####-####";
    public static final String CPF = "###.###.###-##";
    public static final String CNPJ = "##.###.###/####-##";
    public static final String PLACA = "AAA ####";
    public static final String ER3 = "############################################";

    private String formatacaoCampo = "";    // Exemplo: Um campo TELEFONE seria exibido (__) ____-____
    private String conteudoFormatado = "";
    private ArrayList<String> expressao = new ArrayList<String>();
    private String mascara = "";
    private String conteudoSemFormatacao = "";

    /**
     * @param mask - the mask used to format the MaskedTextBox
     *                 <br>Patterns:<br># = [0-9]<br>9 = [\s0-9]<br>A = [A-Z]
     *                 <br>a = [a-z]<br>B = [A-Za-z]<br>C = [A-Za-z0-9]
     *                 <br>Formatting characters = /, * . - : ( ) { } = +
     */
    @UiConstructor
    public MaskedTextBox(String mask) {
        super();
        sinkEvents(Event.ONPASTE);

        this.mascara = mask;
        transformMask();
        assignEvents();
    }

    public String getFormattedText() {
        String formattedText = super.getText();
        
        if (formattedText.equals(this.formatacaoCampo)) {
            formattedText = "";
        }
        
        return formattedText;
    }

    @Override
    public String getText() {
        return this.conteudoSemFormatacao;
    }
    
    
    @Override
    public String getValue() {
    	return this.conteudoSemFormatacao;
    }

    @Override
    public void onBrowserEvent(Event event) {
        super.onBrowserEvent(event);

        if (event.getTypeInt() == Event.ONPASTE) {
            event.preventDefault();
            /*
             *  Os comandos abaixo limpam o campo de texto antes de receber
             *  o novo texto a ser colado:
             */
            conteudoSemFormatacao = "";
            format();
            
            String pastedText = getPastedText(event);
            for (int i = 0; i < pastedText.length(); ++i) {
                addCharCode(pastedText.charAt(i));
            }
        }
    }

    @Override
    public void setText(String text) {
        if (text != null) {
            text = removeFormatting(text);
            conteudoSemFormatacao = conteudoFormatado = text;
        } else {
            conteudoSemFormatacao = conteudoFormatado = "";
        }

        format();
    }
    @Override
    public void setValue(String text) {
    	if (text != null) {
    		text = removeFormatting(text);
    		conteudoSemFormatacao = conteudoFormatado = text;
    	} else {
    		conteudoSemFormatacao = conteudoFormatado = "";
    	}
    	
    	format();
    }

    private void addCharCode(Character ch) {
        String contentToValidate = conteudoSemFormatacao + ch;

        if (contentToValidate.length() <= expressao.size()
                && String.valueOf(ch).matches(
                        expressao.get(contentToValidate.length() - 1))) {
            conteudoSemFormatacao = contentToValidate;
            format();
        } else if (contentToValidate.length() <= expressao.size()
                && String.valueOf(ch).toUpperCase().matches(
                        expressao.get(contentToValidate.length() - 1))){
            conteudoSemFormatacao = contentToValidate.toUpperCase();
            format();
        }
    }

    private void assignEvents() {
        this.addKeyDownHandler(new KeyDownHandler() {
            public void onKeyDown(KeyDownEvent event) {
                if (event.getNativeKeyCode() == KeyCodes.KEY_BACKSPACE || 
                        event.getNativeKeyCode() == KeyCodes.KEY_DELETE) {
                    if (getSelectionLength() >= 1) {
                        conteudoSemFormatacao = "";
                        format();
                    }
                    else if (conteudoSemFormatacao.length() >= 1) {
                        conteudoSemFormatacao = conteudoSemFormatacao.substring(0, conteudoSemFormatacao.length() - 1);
                        format();
                    }

                    cancelKey();
                } else if (event.getNativeKeyCode() == KeyCodes.KEY_LEFT && !event.isShiftKeyDown()) {
                    cancelKey();
                }
            }
        });

        this.addKeyPressHandler(new KeyPressHandler() {
            public void onKeyPress(KeyPressEvent event) {
                Integer nativeCode = event.getNativeEvent().getKeyCode();
                if (nativeCode == KeyCodes.KEY_TAB) {
                    return;
                }

                addCharCode(event.getCharCode());
                cancelKey();
            }
        });

        this.addClickHandler(new ClickHandler() {
            public void onClick(ClickEvent event) {
                setCaretPosition(getElement(), mascara.length());
            }
        });
    }

    private void format() {
        this.conteudoFormatado = this.formatacaoCampo;

        for (Integer i = 0; i < this.conteudoSemFormatacao.length(); i++) {
            this.conteudoFormatado = this.conteudoFormatado.replaceFirst("[_]",
                    this.conteudoSemFormatacao.substring(i, i + 1));
        }

        super.setText(this.conteudoFormatado);
    }

    public static native String getPastedText(Event event) /*-{
            var text = "";

            // This should eventually work in Firefox:
            // https://bugzilla.mozilla.org/show_bug.cgi?id=407983
            if (event.clipboardData) // WebKit (Chrome/Safari)
            {
                try
                {
                    text = event.clipboardData.getData("Text");
                    return text;
                }
                catch (e)
                {
                    // Hmm, that didn't work.
                }
            }

            if ($wnd.clipboardData) // IE
            {
                try
                {
                    text = $wnd.clipboardData.getData("Text");
                    return text;
                }
                catch (e)
                {
                    // Hmm, that didn't work.
                }
            }

            return text;
        }-*/;

    private native void setCaretPosition(Element elem, Integer pos) /*-{
    if (elem.setSelectionRange) {
      elem.setSelectionRange(pos, pos);
    } else if (elem.createTextRange) {
      var range = elem.createTextRange();
      range.collapse = true;
      range.moveStart('character', pos);
      range.moveEnd('character', pos);
      range.select();
    }
  }-*/;

    private void transformMask() {
        for (Integer i = 0; i < this.mascara.toCharArray().length; i++) {
            Character ch = this.mascara.toCharArray()[i];
            switch (ch) {
            case '#':
                this.expressao.add("[0-9]{1}");
                this.conteudoFormatado += "_";
                break;

            case '9':
                this.expressao.add("[\\s0-9]{1}");
                this.conteudoFormatado += "_";
                break;

            case 'A':
                this.expressao.add("[A-Z]{1}");
                this.conteudoFormatado += "_";
                break;

            case 'a':
                this.expressao.add("[a-z]{1}");
                this.conteudoFormatado += "_";
                break;

            case 'B':
                this.expressao.add("[A-Za-z]{1}");
                this.conteudoFormatado += "_";
                break;

            case 'C':
                this.expressao.add("[A-Za-z0-9]{1}");
                this.conteudoFormatado += "_";
                break;

            case '/':
            case ',':
            case '*':
            case '.':
            case '-':
            case ':':
            case '(':
            case ')':
            case '{':
            case '}':
            case '=':
            case '+':
            case ' ':
                this.conteudoFormatado += ch;
                break;
            }
        }

        this.formatacaoCampo = this.conteudoFormatado;
        super.setText(this.conteudoFormatado);
    }

    /**
     * Aplica a nova máscara recebida como parâmetro.
     * @param mask - the mask used to format the MaskedTextBox
     *                 <br>Patterns:<br># = [0-9]<br>9 = [\s0-9]<br>A = [A-Z]
     *                 <br>a = [a-z]<br>B = [A-Za-z]<br>C = [A-Za-z0-9]
     *                 <br>Formatting characters = /, * . - : ( ) { } = +
     */
    public void changeToMask(String mask) {
        this.formatacaoCampo = "";
        this.conteudoFormatado = "";
        this.expressao.clear();
        this.mascara = mask;
        transformMask();
        format();
    }
    
    /**
     * Limpa o texto do campo e aplica a nova máscara recebida como parâmetro.
     * @param mask - the mask used to format the MaskedTextBox
     *                 <br>Patterns:<br># = [0-9]<br>9 = [\s0-9]<br>A = [A-Z]
     *                 <br>a = [a-z]<br>B = [A-Za-z]<br>C = [A-Za-z0-9]
     *                 <br>Formatting characters = /, * . - : ( ) { } = +
     */
    public void clearTextAndChangeMask(String mask) {
        this.formatacaoCampo = "";
        this.conteudoFormatado = "";
        this.expressao.clear();
        this.mascara = mask;
        this.conteudoSemFormatacao = "";
        transformMask();
    }
    
    /**
     * Removes all characters that are not numbers or letters.
     * 
     * @param formattedText formatted date text to remove characters
     * 
     * @return date text unformatted
     */
    private String removeFormatting(String formattedText) {
        return formattedText.replaceAll("[^A-Za-z0-9]", "");
    }
    
}